package org.folio.rest.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import io.vertx.core.AsyncResult;
import io.vertx.core.Context;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.ext.sql.ResultSet;
import org.folio.cql2pgjson.CQL2PgJSON;
import org.folio.rest.RestVerticle;
import org.folio.rest.impl.other.take.TenantsTaskService;
import org.folio.rest.impl.util.FileExport;
import org.folio.rest.jaxrs.model.Errors;
import org.folio.rest.jaxrs.model.PartyDataCollection;
import org.folio.rest.jaxrs.model.PartyDataGroup;
import org.folio.rest.jaxrs.resource.PartyData;
import org.folio.rest.persist.PgUtil;
import org.folio.rest.persist.PostgresClient;
import org.folio.rest.tools.utils.ValidationHelper;

import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static io.vertx.core.Future.succeededFuture;

/**
 * @author lee
 * @Classname PartyDataImpl
 * @Description TODO
 * @Date 2020/6/28 15:24
 * @Created by lee
 */
public class PartyDataImpl implements PartyData {
    private static final Logger logger = LoggerFactory.getLogger("modparty");
    @Override
    public void postPartyData(String lang, org.folio.rest.jaxrs.model.PartyData entity, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {

    }

    @Override
    public void getPartyData(String query, int offset, int limit, String lang, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
        PostgresClient pg = PgUtil.postgresClient(vertxContext, okapiHeaders);

        String cql = null;
        if (!StrUtil.isBlankOrUndefined(query))
        {
            try {
                CQL2PgJSON field = new CQL2PgJSON("a.jsonb");
                cql = field.cql2pgJson(query+" and isCalendar = 0 ");
            }catch (Exception e)
            {
                Errors valErr = ValidationHelper.createValidationErrorMessage("query", query,
                        "Your request query is empty, this is not allowed  ");
              asyncResultHandler.handle(succeededFuture(GetPartyDataResponse.respond422WithApplicationJson(valErr)));
              return;
            }
        }
        HashMap<String, String> mapping = CollUtil.newHashMap();
        mapping.put("party_name", "partyName");
        mapping.put("reserve_amount", "reserveAmount");
        mapping.put("id", "id");
        mapping.put("party_start_date", "partyStartDate");

        mapping.put("attend_amount", "attendAmount");
        mapping.put("absence_amount", "absenceAmount");
        mapping.put("view_amount", "viewAmount");
        mapping.put("leave_amount", "leaveAmount");
        mapping.put("attend_percent", "attendPercent");
        mapping.put("absence_percent", "absencePercent");
        mapping.put("leave_percent", "leavePercent");
        String allSql = createSQL(cql, null, null, okapiHeaders.get(RestVerticle.OKAPI_HEADER_TENANT));
        String finalCql = cql;
        pg.select(allSql, reply->{
            if (reply.succeeded()){
                ResultSet value = reply.result();
                Integer row = value.getNumRows();
                String sql = createSQL(finalCql, offset, limit, okapiHeaders.get(RestVerticle.OKAPI_HEADER_TENANT));
                  pg.select(sql,getReply->{
                      List<PartyDataGroup> partyDataGroupList = new ArrayList<>();
                      ResultSet getReplyValue = getReply.result();
                      getReplyValue.getRows().forEach((item) -> {
                          JsonObject map = item;

                          PartyDataGroup pd = BeanUtil.mapToBean(map.getMap(), PartyDataGroup.class, CopyOptions.create().setFieldMapping(mapping));

                          pd.setViewAmount(""+(Integer.parseInt(pd.getViewAmount())+ TenantsTaskService.getIntCounter(okapiHeaders.get(RestVerticle.OKAPI_HEADER_TENANT),pd.getId())));
                          partyDataGroupList.add(pd);
                      });
                      PartyDataCollection partyDataCollection = new PartyDataCollection();
                      partyDataCollection.setPartyDataGroup(partyDataGroupList);
                      partyDataCollection.setTotalRecords(row);
                      asyncResultHandler.handle(Future.succeededFuture(GetPartyDataResponse.respond200WithApplicationJson(partyDataCollection)));

                  });
                  }else {
                ValidationHelper.handleError(reply.cause(),asyncResultHandler);
            }
        });
    }

    @Override
    public void getPartyDataExportExcel(String query, int offset, int limit, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
        PostgresClient pg = PgUtil.postgresClient(vertxContext, okapiHeaders);

        String cql = null;
        if (!StrUtil.isBlankOrUndefined(query))
        {
            try {
                CQL2PgJSON field = new CQL2PgJSON("a.jsonb");
                cql = field.cql2pgJson(query);
            }catch (Exception e)
            {

            }
        }
        HashMap<String, String> mapping = CollUtil.newHashMap();
        mapping.put("party_name", "活动名称");
        mapping.put("reserve_amount", "报名次数");

        mapping.put("party_start_date", "活动开始时间");

        mapping.put("attend_amount", "签到次数");
        mapping.put("absence_amount", "缺席次数");
        mapping.put("view_amount","活动浏览量");
        mapping.put("leave_amount", "请假次数");
        mapping.put("attend_percent", "签到率");
        mapping.put("absence_percent", "缺席率");
        mapping.put("leave_percent", "请假率");
        String sql = createSQL(cql, null, null, okapiHeaders.get(RestVerticle.OKAPI_HEADER_TENANT));
        pg.select(sql,reply->{
            if (reply.succeeded()){
                ResultSet value = reply.result();
                List<Map<String, Object>> list = value.getRows().stream().map(a->{
                    Map<String, Object> tmp = a.getMap();
                    tmp.remove("id");
                    tmp.put("view_amount",Integer.valueOf((tmp.get("view_amount") ==null?0:Integer.valueOf(""+tmp.get("view_amount")))+TenantsTaskService.getIntCounter(okapiHeaders.get(RestVerticle.OKAPI_HEADER_TENANT),""+tmp.get("id"))) );
                    return tmp;
                }).collect(Collectors.toList());

                FileExport.fileExports(mapping, list, h -> {
                    if (h.succeeded()) {
                        asyncResultHandler.handle(Future.succeededFuture(GetPartyDataExportExcelResponse.respond200WithApplicationOctetStream(h.result())));
                    } else {
                        ValidationHelper.handleError(reply.cause(), asyncResultHandler);
                    }
                });
            }else {
                ValidationHelper.handleError(reply.cause(),asyncResultHandler);
            }
        });
    }




    private static String createSQL(String query ,Integer offset ,Integer limit,String tenant) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(" SELECT f.activityName AS party_name, f.count AS reserve_amount," +
                " f.activityId AS id, f.activityStartDate AS party_start_date, f.checkin AS attend_amount, f.absence AS absence_amount, f.leave AS leave_amount," +
                " CASE WHEN f.count = 0 THEN '0.00%' ELSE concat(round(sum(f.checkin)::numeric / sum(f.count), 2) * 100, '%') END attend_percent," +
                " CASE WHEN f.count = 0 THEN '0.00%' ELSE concat(round(sum(f.absence)::numeric / sum(f.count), 2) * 100, '%') END absence_percent," +
                " CASE WHEN f.count = 0 THEN '0.00%' ELSE concat(round(sum(f.leave)::numeric / sum(f.count), 2) * 100, '%') END leave_percent, " +
                "CASE WHEN s.jsonb ->> 'viewAmount' is null THEN '0' else  s.jsonb ->> 'viewAmount' END view_amount "+
                "FROM(SELECT d.activityId, d.quota, d.activityName, d.activityStartDate, count(d.registrationId) AS count, " +
                "COUNT(case WHEN d.act_check_in = '1' THEN d.activityName end) AS checkin, " +
                "COUNT(case WHEN d.act_check_in = '0' THEN d.activityName end) AS absence, " +
                "COUNT(case WHEN d.act_check_in = '3' THEN d.activityName end) AS leave " +

                "FROM (SELECT a.jsonb ->> 'id' AS activityId, a.jsonb ->> 'partyName' AS activityName, " +
                "a.jsonb ->> 'quota' AS quota, s.jsonb ->> 'attendState' AS act_check_in, a.jsonb ->> 'propertyName', " +
                "a.jsonb ->> 'partyStartDate' AS activityStartDate, c.id AS registrationId FROM " +
                tenant+"_mod_party.party AS a LEFT JOIN "+tenant+"_mod_party.reserve AS c ON a.jsonb ->> 'id' = c.jsonb ->> 'partyId' LEFT JOIN " +
                tenant+"_mod_party.attend AS s ON c.jsonb ->> 'id' = s.jsonb ->> 'reserveId' " +
                "WHERE a.jsonb ->> 'isDel' = '0' ");
        if (!StrUtil.isBlankOrUndefined(query))
        {
            stringBuffer.append(" AND "+query);
        }
        stringBuffer.append(" ) AS d GROUP BY d.activityId, d.quota, d.activityName, d.activityStartDate) AS f ");
        stringBuffer.append(" LEFT JOIN "+tenant+"_mod_party.party_view_amount AS s on f.activityId = s.jsonb->> 'id' ");
        stringBuffer.append(" GROUP BY f.activityId, f.quota, f.checkin, f.absence, f.leave, f.activityStartDate, f.count, f.activityName,s.jsonb->>'viewAmount' ");
        if (offset !=null && limit !=null)
        {
            stringBuffer.append(" limit " + limit + " offset " + offset + " ;");
        }
        logger.info(" run SQL :{}",stringBuffer.toString());
        return stringBuffer.toString();
    }

}
